package com.hongshu.web.service.web.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hongshu.common.core.constant.AuthConstant;
import com.hongshu.common.core.constant.Constants;
import com.hongshu.common.core.constant.TokenConstant;
import com.hongshu.common.core.enums.ResultCodeEnum;
import com.hongshu.common.core.enums.TerminalTypeEnum;
import com.hongshu.common.core.exception.HongshuException;
import com.hongshu.common.core.utils.*;
import com.hongshu.common.core.utils.ip.AddressUtils;
import com.hongshu.common.core.utils.ip.IpUtils;
import com.hongshu.web.async.SmsAsyncService;
import com.hongshu.web.domain.dto.AuthUserDTO;
import com.hongshu.web.domain.entity.WebIpBlacklist;
import com.hongshu.web.domain.entity.WebSystemConfig;
import com.hongshu.web.domain.entity.WebUser;
import com.hongshu.web.domain.vo.QrCodeConfirmVO;
import com.hongshu.web.manager.AsyncManager;
import com.hongshu.web.manager.factory.AsyncFactory;
import com.hongshu.web.mapper.sys.SysSystemConfigMapper;
import com.hongshu.web.mapper.web.WebIpBlacklistMapper;
import com.hongshu.web.mapper.web.WebUserMapper;
import com.hongshu.web.service.web.IWebAuthUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * 权限
 *
 * @author: hongshu
 */
@Slf4j
@Service
public class WebAuthUserServiceImpl extends ServiceImpl<WebUserMapper, WebUser> implements IWebAuthUserService {

    @Resource
    private RedisUtils redisUtils;
    @Autowired
    private WebIpBlacklistMapper ipBlacklistMapper;
    @Autowired
    private SysSystemConfigMapper systemConfigMapper;
    @Autowired
    private CaptchaService captchaService;
    @Autowired
    private SmsAsyncService smsAsyncService;


    // Redis中验证码的key前缀
    private static final String VERIFY_CODE_PREFIX = "sms:verify:";
    private static final String VERIFY_IP_PREFIX = "sms:ip:";
    // 验证码有效期（分钟）
    private static final long VERIFY_CODE_EXPIRE_MINUTES = 5;
    // 同一手机号发送验证码的间隔时间（秒）
    private static final long SEND_CODE_INTERVAL_SECONDS = 60;


    /**
     * 用户登录
     *
     * @param authUserDTO 用户
     */
    @Override
    @Transactional
    public Map<String, Object> login(AuthUserDTO authUserDTO) {
        Map<String, Object> map = new HashMap<>();
        if (StringUtils.isBlank(authUserDTO.getPhone())) {
            throw new HongshuException(ResultCodeEnum.FAIL);
        }
        // 获取验证码配置，判断是否需要滑块校验
        boolean captchaEnabled = this.isCaptchaEnabled();
        // 如果验证码功能启用，才进行滑块校验
        if (captchaEnabled) {
            // 校验滑块
            CaptchaVO captchaVO = new CaptchaVO();
            captchaVO.setCaptchaVerification(authUserDTO.getCaptchaVerification());
            ResponseModel response = captchaService.verification(captchaVO);
            log.info("密码登录 - captchaVerification: {}", authUserDTO.getCaptchaVerification());
            log.info("密码登录 - captchaService response: {}", response);
            if (!"0000".equals(response.getRepCode())) {
                throw new HongshuException(ResultCodeEnum.ERROR_BLOCKPUZZLE);
            }
        } else {
            log.info("验证码功能已禁用，跳过滑块校验");
        }
        // 查询用户
        WebUser authUser = this.getOne(new QueryWrapper<WebUser>().eq("phone", authUserDTO.getPhone()));
        // 用户不存在-自动注册新用户
        if (authUser == null) {
            authUser = this.register(authUserDTO);
        }
        // 用户存在-登录
        else {
            // 校验用户是否封禁
            if ("1".equals(authUser.getStatus())) {
                throw new HongshuException(ResultCodeEnum.ERROR_ACCOUNT_SUSPENDED);
            }
            // 校验IP是否合法
            boolean exists = ipBlacklistMapper.exists(
                    new QueryWrapper<WebIpBlacklist>().eq("ip_address", IpUtils.getIpAddr())
            );
            if (exists) {
                throw new HongshuException(ResultCodeEnum.ERROR_IP_BANNED);
            }
            // 校验密码是否正确
            if (!authUser.getPassword().equals(SecureUtil.md5(authUserDTO.getPassword()))) {
                throw new HongshuException(ResultCodeEnum.ERROR_PASSWORD);
            }
            authUser.setLoginDate(new Date());
            authUser.setLoginIp(IpUtils.getIpAddr());
            authUser.setAddress(AddressUtils.getRealAddressByIP(authUser.getLoginIp()));
            this.updateById(authUser);
        }
        // Redis缓存Token
        this.setUserInfoAndToken(map, authUser, authUserDTO.getTerminal());
        // 保存日志
        AsyncManager.me().execute(AsyncFactory.recordLoginInfor(
                authUserDTO.getTerminal(),
                authUser.getId(),
                authUser.getUsername(),
                Constants.LOGIN_SUCCESS,
                MessageUtils.message("user.login.success")));
        return map;
    }

    /**
     * 验证码登录
     *
     * @param authUserDTO 用户
     */
    @Override
    public Map<String, Object> loginByCode(AuthUserDTO authUserDTO) {
        Map<String, Object> map = new HashMap<>();
        // 1. 参数校验
        String phone = authUserDTO.getPhone();
        if (StringUtils.isBlank(phone)) {
            throw new HongshuException("手机号不能为空");
        }
        String code = authUserDTO.getCode();
        if (StringUtils.isBlank(code)) {
            throw new HongshuException("验证码不能为空");
        }
        // 4. 手机号验证码登录
        this.verifyCode(phone, code);
        // 5. 获取用户信息
        WebUser currentUser = this.getUserByPhone(phone);
        // 用户不存在-自动注册新用户
        if (ObjectUtil.isEmpty(currentUser)) {
            currentUser = this.register(authUserDTO);
        }
        // 用户存在-登录
        else {
            // 校验用户是否封禁
            if ("1".equals(currentUser.getStatus())) {
                throw new HongshuException(ResultCodeEnum.ERROR_ACCOUNT_SUSPENDED);
            }
            // 校验IP是否合法
            boolean exists = ipBlacklistMapper.exists(
                    new QueryWrapper<WebIpBlacklist>().eq("ip_address", IpUtils.getIpAddr())
            );
            if (exists) {
                throw new HongshuException(ResultCodeEnum.ERROR_IP_BANNED);
            }
            currentUser.setLoginDate(new Date());
            currentUser.setLoginIp(IpUtils.getIpAddr());
            currentUser.setAddress(AddressUtils.getRealAddressByIP(currentUser.getLoginIp()));
            this.updateById(currentUser);
        }
        // 6. Redis缓存Token
        this.setUserInfoAndToken(map, currentUser, authUserDTO.getTerminal());
        log.info("用户验证码登录成功: account={}, userId={}", phone, currentUser.getId());
        // 7. 保存日志
        AsyncManager.me().execute(AsyncFactory.recordLoginInfor(
                authUserDTO.getTerminal(),
                currentUser.getId(),
                currentUser.getUsername(),
                Constants.LOGIN_SUCCESS,
                MessageUtils.message("user.login.success")));
        return map;
    }

    /**
     * 发送验证码（手机号维度+IP维度双重限制）
     *
     * @param phone 手机号
     */
    @Override
    public boolean sendVerifyCode(String phone, String captchaVerification) {
        // 1. 校验手机号
        if (StringUtils.isEmpty(phone)) {
            throw new HongshuException(ResultCodeEnum.ERROR_PHONE);
        }
        // 2. 获取验证码配置，判断是否需要滑块校验
        boolean captchaEnabled = this.isCaptchaEnabled();
        // 3. 如果验证码功能启用，才进行滑块校验
        if (captchaEnabled) {
            if (StringUtils.isBlank(captchaVerification)) {
                throw new HongshuException("滑块验证码不能为空");
            }
            // 校验滑块
            CaptchaVO captchaVO = new CaptchaVO();
            captchaVO.setCaptchaVerification(captchaVerification);
            ResponseModel response = captchaService.verification(captchaVO);
            log.info("发送验证码 - captchaVerification: {}", captchaVerification);
            log.info("发送验证码 - captchaService response: {}", response);
            if (!"0000".equals(response.getRepCode())) {
                throw new HongshuException(ResultCodeEnum.ERROR_BLOCKPUZZLE);
            }
        } else {
            log.info("验证码功能已禁用，跳过滑块校验");
        }
        // 4. IP限流
        String ip = IpUtils.getIpAddr();
        String ipKey = VERIFY_IP_PREFIX + ip;
        Long ipCount = redisUtils.incrBy(ipKey, 1);
        if (ipCount == 1) {
            redisUtils.expire(ipKey, 1, TimeUnit.HOURS); // 设置1小时过期
        }
        if (ipCount > 20) {
            throw new HongshuException(ResultCodeEnum.ERROR_IP_COUNT);
        }
        // 5. 检查是否在限制时间内
        String intervalKey = VERIFY_CODE_PREFIX + "interval:" + phone;
        if (!redisUtils.setIfAbsent(intervalKey, "1", SEND_CODE_INTERVAL_SECONDS)) {
            log.warn("发送验证码太频繁: phone={}", phone);
            throw new HongshuException(ResultCodeEnum.ERROR_CODE_COUNT);
        }
        // 6. 生成验证码
        String code = this.generateRandomCode();
        // 7. 异步发送短信
        smsAsyncService.sendVerificationCodeAsync(phone, intervalKey, code);
        return true;
    }

    /**
     * 判断验证码功能是否启用
     */
    private boolean isCaptchaEnabled() {
        try {
            // 从配置表中获取验证码开关状态
            WebSystemConfig config = systemConfigMapper
                    .selectOne(new LambdaQueryWrapper<WebSystemConfig>()
                            .eq(WebSystemConfig::getConfigKey, "aj.captcha.enabled"));

            if (config != null) {
                return "1".equals(config.getConfigValue());
            }
            // 如果配置不存在，默认启用验证码（保持向后兼容）
            return true;
        } catch (Exception e) {
            log.error("获取验证码配置失败", e);
            // 出现异常时默认启用验证码，确保安全性
            return true;
        }
    }

    /**
     * 演示账户登录
     *
     * @param authUserDTO 用户
     */
    @Override
    @Transactional
    public Map<String, Object> demoLogin(AuthUserDTO authUserDTO) {
        Map<String, Object> map = new HashMap<>();
        if (StringUtils.isBlank(authUserDTO.getPhone())) {
            throw new HongshuException(ResultCodeEnum.FAIL);
        }
        // 查询用户
        WebUser authUser = this.getOne(new QueryWrapper<WebUser>().eq("phone", authUserDTO.getPhone()));
        // 校验用户是否封禁
        if ("1".equals(authUser.getStatus())) {
            throw new HongshuException(ResultCodeEnum.ERROR_ACCOUNT_SUSPENDED);
        }
        // 校验IP是否合法
        boolean exists = ipBlacklistMapper.exists(
                new QueryWrapper<WebIpBlacklist>().eq("ip_address", IpUtils.getIpAddr())
        );
        if (exists) {
            throw new HongshuException(ResultCodeEnum.ERROR_IP_BANNED);
        }
        // 校验密码是否正确
        if (!authUser.getPassword().equals(SecureUtil.md5(authUserDTO.getPassword()))) {
            throw new HongshuException(ResultCodeEnum.ERROR_PASSWORD);
        }
        authUser.setLoginDate(new Date());
        authUser.setLoginIp(IpUtils.getIpAddr());
        authUser.setAddress(AddressUtils.getRealAddressByIP(authUser.getLoginIp()));
        this.updateById(authUser);
        // Redis缓存Token
        this.setUserInfoAndToken(map, authUser, authUserDTO.getTerminal());
        // 保存日志
        AsyncManager.me().execute(AsyncFactory.recordLoginInfor(
                authUserDTO.getTerminal(),
                authUser.getId(),
                authUser.getUsername(),
                Constants.LOGIN_SUCCESS,
                MessageUtils.message("user.login.success")));
        return map;
    }

    /**
     * 生成短信验证码
     */
    private String generateRandomCode() {
        return String.format("%06d", new Random().nextInt(1000000));
    }

    /**
     * 验证码校验
     */
    private void verifyCode(String phone, String code) {
        String key = VERIFY_CODE_PREFIX + "code:" + phone;
        String savedCode = redisUtils.get(key);
        if (StringUtils.isEmpty(savedCode)) {
            log.warn("验证码不存在或已过期: account={}", phone);
            throw new HongshuException(ResultCodeEnum.ERROR_CODE_NULL);
        }
        if (!code.equals(savedCode)) {
            log.warn("验证码有误，请检查重新输入: account={}", phone);
            throw new HongshuException(ResultCodeEnum.ERROR_CODE_ERROR);
        }
        // 验证通过后删除验证码
        redisUtils.delete(key);
    }

    /**
     * 根据账号获取用户
     */
    private WebUser getUserByPhone(String phone) {
        return this.getOne(new LambdaQueryWrapper<WebUser>()
                .eq(WebUser::getPhone, phone)
                .or()
                .eq(WebUser::getEmail, phone)
                .last("LIMIT 1"));
    }

    /**
     * 注册新用户
     */
    private WebUser registerUser(AuthUserDTO authUserDTO) {
        return this.register(authUserDTO);
    }


    /**
     * 根据token获取当前用户
     *
     * @param accessToken accessToken
     */
    @Override
    public WebUser getUserInfoByToken(String accessToken) {
        String userId = JwtUtils.getUserId(accessToken);
        return this.getById(userId);
    }

    /**
     * 用户注册
     *
     * @param authUserDTO 前台传递用户信息
     */
    @Override
    public WebUser register(AuthUserDTO authUserDTO) {
        // 先查一遍，防止并发下重复注册
        WebUser exist = this.getOne(new QueryWrapper<WebUser>().eq("phone", authUserDTO.getPhone()));
        if (exist != null) {
            // 可以直接返回 exist，或者抛出“手机号已注册”异常
            throw new HongshuException("手机号已注册");
        }
        WebUser user = ConvertUtils.sourceToTarget(authUserDTO, WebUser.class);
        user.setFromType(TerminalTypeEnum.fromValue(authUserDTO.getTerminal()).getCode());
        user.setHsId(Long.valueOf(RandomUtil.randomNumbers(10)));
        user.setUsername(this.generateUsername());
        user.setAvatar(AuthConstant.DEFAULT_AVATAR);
        user.setUserCover(AuthConstant.DEFAULT_COVER);
        user.setPassword(SecureUtil.md5(authUserDTO.getPassword()));
        user.setLoginIp(IpUtils.getIpAddr());
        user.setAddress(AddressUtils.getRealAddressByIP(user.getLoginIp()));
        user.setLoginDate(new Date());
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        this.save(user);
        return user;
    }

    public String generateUsername() {
        String prefix = "小红薯";
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            sb.append(chars.charAt(random.nextInt(chars.length())));
        }
        return prefix + sb;
    }

    /**
     * 用户是否注册
     *
     * @param authUserDTO 用户
     */
    @Override
    public boolean isRegister(AuthUserDTO authUserDTO) {
        long count = this.count(new QueryWrapper<WebUser>().eq("phone", authUserDTO.getPhone()).or().eq("email", authUserDTO.getEmail()));
        return count > 0;
    }

    /**
     * 退出登录
     *
     * @param userId 用户ID
     */
    @Override
    public void loginOut(String userId, String terminal) {
        // 检查userId是否为空
        if (userId == null || userId.trim().isEmpty()) {
            log.warn("退出登录失败：用户ID为空");
            return;
        }

        String userKey = AuthConstant.USER_KEY + userId;
        String refreshTokenStartTimeKey = AuthConstant.REFRESH_TOKEN_START_TIME + userId;
        List<String> keyList = new ArrayList<>();
        keyList.add(userKey);
        keyList.add(refreshTokenStartTimeKey);
        redisUtils.delete(keyList);

        // 保存日志
        WebUser authUser = this.getOne(new QueryWrapper<WebUser>().eq("id", userId));
        if (authUser != null) {
            AsyncManager.me().execute(AsyncFactory.recordLoginInfor(
                    terminal,
                    authUser.getId(),
                    authUser.getUsername(),
                    Constants.LOGOUT,
                    MessageUtils.message("user.logout.success")));
        } else {
            log.warn("退出登录失败：找不到用户ID为 {} 的用户记录", userId);
        }
    }

    /**
     * 修改密码
     *
     * @param authUserDTO 用户
     */
    @Override
    public boolean updatePassword(AuthUserDTO authUserDTO) {
        if (!authUserDTO.getPassword().equals(authUserDTO.getCheckPassword())) {
            return false;
        }
        String pwd = SecureUtil.md5(authUserDTO.getPassword());
        WebUser user;
        if (StringUtils.isBlank(authUserDTO.getId())) {
            user = this.getOne(new QueryWrapper<WebUser>().eq("phone", authUserDTO.getPhone()).or().eq("email", authUserDTO.getEmail()));
        } else {
            user = this.getById(authUserDTO.getId());
        }
        user.setPassword(pwd);
        return this.updateById(user);
    }

    /**
     * 刷新token
     *
     * @param refreshToken refreshToken
     */
    @Override
    public Map<String, Object> refreshToken(String refreshToken) {
        HashMap<String, Object> res = new HashMap<>(2);
        String userId = JwtUtils.getUserId(refreshToken);
        log.info("userId:{}", userId);
        // 创建新的accessToken
        String accessToken = JwtUtilss.getJwtToken(userId, AuthConstant.ACCESS_TOKEN_EXPIRATION_TIME);

        // minTimeOfRefreshToken  最短刷新时间
        // refreshTokenStartTime  刷新令牌开始时间

        // 下面判断是否刷新 refreshToken，如果refreshToken 快过期了 需要重新生成一个替换掉
        // refreshToken 有效时长是应该为accessToken有效时长的2倍
        long minTimeOfRefreshToken = 2 * AuthConstant.ACCESS_TOKEN_EXPIRATION_TIME;
        String refreshTokenStr = redisUtils.get(AuthConstant.REFRESH_TOKEN_START_TIME + userId);

        if (StringUtils.isBlank(refreshTokenStr)) {
            return res;
        }

        // refreshToken创建的起始时间点
        long refreshTokenStartTime = Long.parseLong(refreshTokenStr);
        // (refreshToken上次创建的时间点 + refreshToken的有效时长 - 当前时间点) 表示refreshToken还剩余的有效时长，如果小于2倍accessToken时长 ，则刷新 refreshToken
        if (refreshTokenStartTime + AuthConstant.REFRESH_TOKEN_EXPIRATION_TIME - System.currentTimeMillis() <= minTimeOfRefreshToken) {
            // 刷新refreshToken
            refreshToken = JwtUtilss.getJwtToken(userId, refreshTokenStartTime);
            redisUtils.setEx(AuthConstant.REFRESH_TOKEN_START_TIME + userId, String.valueOf(System.currentTimeMillis()), AuthConstant.REFRESH_TOKEN_EXPIRATION_TIME, TimeUnit.MILLISECONDS);
        }

        res.put(TokenConstant.ACCESS_TOKEN, accessToken);
        res.put(TokenConstant.REFRESH_TOKEN, refreshToken);
        return res;
    }

    /**
     * 生成二维码
     */
    @Override
    public Map<String, Object> getQrCode() {
        // 1. 生成唯一token
        String loginToken = UUID.randomUUID().toString();
        // 2. 存入Redis，设置有效期（如5分钟）
        redisUtils.set(loginToken, "pending", 300);

        // 3. 生成二维码内容（直接包含loginToken，方便UniApp解析）
        String qrContent = "hongshu://login?loginToken=" + loginToken;

        // 4. 生成二维码图片（base64）
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        // 使用Hutool生成二维码
        QrCodeUtil.generate(qrContent, 300, 300, "png", out);
        String base64 = Base64.getEncoder().encodeToString(out.toByteArray());
        String qrcodeUrl = "data:image/png;base64," + base64;

        // 5. 返回给前端
        Map<String, Object> result = new HashMap<>();
        result.put("loginToken", loginToken);
        result.put("qrcodeUrl", qrcodeUrl);
        return result;
    }

    /**
     * 校验数据
     */
    private boolean checkCode(AuthUserDTO authUserDTO) {
        String code = "";
        if (StringUtils.isNotBlank(redisUtils.get(AuthConstant.CODE + authUserDTO.getPhone()))) {
            code = redisUtils.get(AuthConstant.CODE + authUserDTO.getPhone());
        } else if (StringUtils.isNotBlank(redisUtils.get(AuthConstant.CODE + authUserDTO.getEmail()))) {
            code = redisUtils.get(AuthConstant.CODE + authUserDTO.getEmail());
        }
        return StringUtils.isBlank(code) || !code.equals(authUserDTO.getCode());
    }

    /**
     * 缓存token
     */
    private void setUserInfoAndToken(Map<String, Object> map, WebUser authUser, String terminal) {
        String accessToken = JwtUtilss.getJwtToken(String.valueOf(authUser.getId()), AuthConstant.ACCESS_TOKEN_EXPIRATION_TIME);
        String refreshToken = JwtUtilss.getJwtToken(String.valueOf(authUser.getId()), AuthConstant.REFRESH_TOKEN_EXPIRATION_TIME);
        //缓存当前登录用户 refreshToken 创建的起始时间，这个会在刷新accessToken方法中 判断是否要重新生成(刷新)refreshToken时用到
        redisUtils.setEx(AuthConstant.REFRESH_TOKEN_START_TIME + authUser.getId(), String.valueOf(System.currentTimeMillis()), AuthConstant.REFRESH_TOKEN_EXPIRATION_TIME, TimeUnit.MILLISECONDS);
        //将用户信息保存在redis中
        map.put(TokenConstant.ACCESS_TOKEN, accessToken);
        map.put(TokenConstant.REFRESH_TOKEN, refreshToken);
        map.put(AuthConstant.USER_INFO, authUser);
    }

    /**
     * 移动端扫描二维码（第一步）
     */
    public Map<String, Object> scanQrCode(String loginToken) {
        Map<String, Object> result = new HashMap<>();

        // 检查二维码是否还有效
        String status = redisUtils.get(loginToken);
        log.info("扫描二维码 - loginToken: {}, Redis状态: {}", loginToken, status);

        if (status == null) {
            log.info("二维码已失效 - loginToken: {}, 原因: Redis中无数据", loginToken);
            result.put("success", false);
            result.put("message", "二维码已失效");
            return result;
        }

        // 如果状态是"pending"，标记为已扫描
        if ("pending".equals(status)) {
            log.info("二维码状态为pending，标记为已扫描 - loginToken: {}", loginToken);
            redisUtils.set(loginToken, "scanned", 120); // 再保留2分钟
            result.put("success", true);
            result.put("message", "扫描成功，等待确认");
            return result;
        }

        // 如果状态是"scanned"，说明已经扫描过了，直接返回成功
        if ("scanned".equals(status)) {
            log.info("二维码状态为scanned，直接返回成功 - loginToken: {}", loginToken);
            result.put("success", true);
            result.put("message", "已扫描，等待确认登录");
            return result;
        }

        // 其他状态（如"confirmed"或用户ID），说明已经登录过了
        log.info("二维码状态异常 - loginToken: {}, 状态: {}", loginToken, status);
        result.put("success", false);
        result.put("message", "二维码已失效");
        return result;
    }

    /**
     * 移动端确认二维码登录（第二步）
     */
    @Override
    public Map<String, Object> confirmQrCodeLogin(QrCodeConfirmVO vo) {
        Map<String, Object> result = new HashMap<>();
        String loginToken = vo.getLoginToken();
        String userId = vo.getUserId();

        // 检查二维码是否已扫描
        String status = redisUtils.get(loginToken);
        if (status == null || !"scanned".equals(status)) {
            result.put("success", false);
            result.put("message", "请先扫描二维码");
            return result;
        }

        // 标记为已确认，存userId
        redisUtils.set(loginToken, userId, 120); // 再保留2分钟
        result.put("success", true);
        result.put("message", "确认登录成功");
        return result;
    }

    /**
     * 检查二维码状态
     */
    @Override
    public Map<String, Object> checkQrCodeStatus(String loginToken) {
        Map<String, Object> result = new HashMap<>();
        String val = redisUtils.get(loginToken);

        if (val == null) {
            result.put("status", "expired");
            result.put("message", "二维码已过期");
        } else if ("pending".equals(val)) {
            result.put("status", "pending");
            result.put("message", "等待扫码");
        } else if ("scanned".equals(val)) {
            result.put("status", "scanned");
            result.put("message", "等待确认登录");
        } else {
            // val为userId，已确认
            result.put("status", "confirmed");
            result.put("userId", val);
            result.put("message", "登录成功");
        }

        return result;
    }

    /**
     * 二维码登录成功，获取用户信息和token
     */
    @Override
    public Map<String, Object> qrCodeLogin(String userId) {
        Map<String, Object> result = new HashMap<>();

        try {
            // 根据userId获取用户信息
            WebUser user = baseMapper.selectById(userId);
            if (user == null) {
                result.put("success", false);
                result.put("message", "用户不存在");
                return result;
            }

            // 生成token
            String accessToken = JwtUtilss.getJwtToken(user.getId().toString(), 24 * 60 * 60 * 1000L); // 24小时
            String refreshToken = JwtUtilss.getJwtToken(user.getId().toString(), 7 * 24 * 60 * 60 * 1000L); // 7天

            // 构建用户信息
            Map<String, Object> userInfo = new HashMap<>();
            userInfo.put("id", user.getId());
            userInfo.put("username", user.getUsername());
            userInfo.put("nickname", user.getUsername()); // 使用username作为nickname
            userInfo.put("avatar", user.getAvatar());
            userInfo.put("email", user.getEmail());
            userInfo.put("phone", user.getPhone());

            // 返回结果
            result.put("accessToken", accessToken);
            result.put("refreshToken", refreshToken);
            result.put("userInfo", userInfo);
            result.put("success", true);
            result.put("message", "登录成功");

            log.info("二维码登录成功 - userId: {}, username: {}", userId, user.getUsername());

        } catch (Exception e) {
            log.error("二维码登录失败 - userId: {}", userId, e);
            result.put("success", false);
            result.put("message", "登录失败");
        }

        return result;
    }
}
